package org.feng.common;

import lombok.extern.slf4j.Slf4j;
import org.feng.common.util.GsonUtil;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.AsyncHandlerMethodReturnValueHandler;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

/**
 * 自定义方法返回结果处理器
 *
 * @version v1.0
 * @author: fengjinsong
 * @date: 2023年08月26日 14时15分
 */
@Slf4j
public class CustomHandlerMethodReturnValueHandler implements HandlerMethodReturnValueHandler, AsyncHandlerMethodReturnValueHandler {
    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        // 方法上标注了ResponseBody，就处理
        return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || returnType.hasMethodAnnotation(ResponseBody.class))
                // 当前方法返回值需要数据脱敏
                && (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), MaskedMethod.class) || returnType.hasMethodAnnotation(MaskedMethod.class));
    }

    @Override
    public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
        mavContainer.setRequestHandled(true);
        HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
        assert response != null;
        response.setContentType("application/json;charset=utf-8");

        if (returnValue instanceof ResponseEntity) {
            ResponseEntity<?> responseEntity = (ResponseEntity<?>) returnValue;
            Object data = responseEntity.getData();
            // 响应结果是集合
            if (data instanceof List) {
                List<?> dataList = (List<?>) data;
                // 集合为空，直接返回
                if (CollectionUtils.isEmpty(dataList)) {
                    response.getWriter().write(GsonUtil.toJsonWithNull(returnValue));
                    return;
                }
                // 处理集合结果集
                Class<?> singleDataClass = dataList.get(0).getClass();
                // 是否标注为脱敏数据实体
                boolean maskedEntity = singleDataClass.isAnnotationPresent(MaskedEntity.class);
                // 不是脱敏实体，不用处理
                if (!maskedEntity) {
                    response.getWriter().write(GsonUtil.toJsonWithNull(returnValue));
                    return;
                }

                // 处理数据脱敏
                for (Object singleData : dataList) {
                    Field[] fields = singleDataClass.getDeclaredFields();
                    for (Field field : fields) {
                        doMaskedField(field, singleData);
                    }
                }
                response.getWriter().write(GsonUtil.toJsonWithNull(returnValue));
                return;
            } else {
                // 非集合响应结果处理
                Class<?> dataClass = data.getClass();
                // 是否标注为脱敏数据实体
                boolean maskedEntity = dataClass.isAnnotationPresent(MaskedEntity.class);
                // 不是脱敏实体，不用处理
                if (!maskedEntity) {
                    response.getWriter().write(GsonUtil.toJsonWithNull(returnValue));
                    return;
                }
                // 脱敏数据
                Field[] fields = dataClass.getDeclaredFields();
                for (Field field : fields) {
                    doMaskedField(field, data);
                }
            }
        }

        // 序列化响应
        response.getWriter().write(GsonUtil.toJsonWithNull(returnValue));
    }

    /**
     * 脱敏数据处理：支持复杂对象（嵌套）
     *
     * @param field  属性对象
     * @param object 对象本身
     */
    private void doMaskedField(Field field, Object object) {
        // 标注了MaskedField
        boolean maskedField = field.isAnnotationPresent(MaskedField.class);
        if (maskedField) {
            doSimpleMaskedField(field, object);
            return;
        }

        // 标注了MaskedComplexField
        boolean maskedComplexField = field.isAnnotationPresent(MaskedComplexField.class);
        if (maskedComplexField) {
            doComplexMaskedField(field, object);
        }
    }

    /**
     * 处理简单字段，比如字符串，数值。直接处理
     *
     * @param field  属性
     * @param object 对象本身
     */
    private void doSimpleMaskedField(Field field, Object object) {
        field.setAccessible(true);
        MaskedField maskedFieldAnnotation = field.getAnnotation(MaskedField.class);
        // 脱敏数据
        try {
            SensitiveDataTypeEnum dataTypeEnum = maskedFieldAnnotation.type();
            Object fieldValue = field.get(object);
            if (Objects.nonNull(fieldValue)) {
                field.set(object, dataTypeEnum.doDecryptAndMaskedField(fieldValue.toString(), maskedFieldAnnotation.sensitiveType()));
            }
        } catch (IllegalAccessException e) {
            log.error("脱敏失败", e);
            throw new RuntimeException("脱敏失败");
        }
        field.setAccessible(false);
    }

    /**
     * 处理复杂字段，比如属性本身是一个对象
     *
     * @param field  属性
     * @param object 对象本身
     */
    private void doComplexMaskedField(Field field, Object object) {
        try {
            boolean needMasked = field.isAnnotationPresent(MaskedField.class) || field.isAnnotationPresent(MaskedComplexField.class);
            if (!needMasked) {
                return;
            }
            // 需要处理的数据本身
            if (field.isAnnotationPresent(MaskedField.class)) {
                doSimpleMaskedField(field, object);
                return;
            }
            field.setAccessible(true);
            Object fieldValue = field.get(object);
            // 属性值没有实际内容
            if (fieldValue == null || (fieldValue instanceof Collection && CollectionUtils.isEmpty((Collection<?>) fieldValue))) {
                return;
            }

            // 处理返回结果是list的场景
            if (fieldValue instanceof List) {
                List<?> fieldValueList = (List<?>) fieldValue;
                Object firstValue = fieldValueList.get(0);
                Class<?> valueClass = firstValue.getClass();
                if (!valueClass.isAnnotationPresent(MaskedEntity.class)) {
                    return;
                }
                Field[] subFields = valueClass.getDeclaredFields();
                for (Object value : fieldValueList) {
                    for (Field subField : subFields) {
                        doComplexMaskedField(subField, value);
                    }
                }
                return;
            }

            // 不是集合
            Class<?> fieldValueClass = fieldValue.getClass();
            if (!fieldValueClass.isAnnotationPresent(MaskedEntity.class)) {
                return;
            }
            Field[] fields = fieldValueClass.getDeclaredFields();
            for (Field subField : fields) {
                doComplexMaskedField(subField, fieldValue);
            }
        } catch (IllegalAccessException e) {
            log.error("脱敏失败", e);
            throw new RuntimeException(e);
        } finally {
            field.setAccessible(false);
        }
    }

    @Override
    public boolean isAsyncReturnValue(Object returnValue, MethodParameter returnType) {
        return supportsReturnType(returnType);
    }
}
